#include "DomValidator.h"

DomValidator::DomValidator(string path){
	this->textFile = new TextFile(path,false);
	vector<string> file;
	while(!this->textFile->isEof()){
		string aux;
		this->textFile->leerLinea(aux);
		if(strcmp(aux.c_str(),"")!=0)
			file.push_back(aux);
	}
	for(int i=0;i<file.size();i++){
		generateStructure(i,file);
	}
}
void DomValidator::generateStructure(int seek,vector<string>& file){
	
	char* deep;
	string value;
	char* line= (char*)malloc(file[seek].size()+1);
	strcpy(line,file[seek].c_str());
	deep = strtok (line,"|:");
	value = strtok (NULL,"|:");
	int currentDeep=atoi(deep);
	if(this->structure[currentDeep]==NULL){
		this->structure[currentDeep]=new map<string, list<string>*>();
	}
	if((*this->structure[currentDeep])[value]==NULL){
		(*this->structure[currentDeep])[value]=new list<string>();
	}
	int childDeep=currentDeep+1;
	int i=1;
	while(currentDeep<childDeep && file.size()>seek+i){
		char* childLine= (char*)malloc(file[seek+i].size()+1);
		strcpy(childLine,file[seek+i].c_str());
		deep = strtok (childLine,"|");
		string childvalue = strtok (NULL,"|");
		childDeep=atoi(deep);
		if(childDeep==currentDeep+1){
			(*this->structure[currentDeep])[value]->insert((*this->structure[currentDeep])[value]->end(),childvalue);		
		}
		i++;
		free(childLine);
	}
	free(line);
}

DomValidator::~DomValidator(){
	delete this->textFile;
	 map<int, map<string, list<string>*>*>::iterator it;
	 for (it = this->structure.begin(); it != this->structure.end(); ++it) {
		map<string, list<string>*>::iterator it2;
		for (it2 = it->second->begin(); it2 != it->second->end(); ++it2) {
			delete it2->second;
		}
		delete it->second;
	 }
}
void DomValidator::validateChilds(XMLElement* element, int deep, Log*log){
	
	list<string>* allowedElements= (*this->structure[deep])[(*element->getName())];
	if(allowedElements==NULL){
		string error="El elemento raiz '";
		error.append((*element->getName()));
		error.append("' es incorrecto.");
		throw XmlSemanticException(error);
	}

	list<XMLElement>& childrens = element->getChildren();
	list<XmlAttribute>& attributes = element->getAttributes();
	list<string>* onlyOneMultiplicity= new list<string>();
	map<string,string>* notPresentElements= new map<string,string>();
	
	for(list<string>::iterator allowedElement = allowedElements->begin();allowedElement != allowedElements->end(); allowedElement++){
		char values[200];
		strcpy(values,allowedElement->c_str());
		char* name = strtok (values,":");
		char* multiplicity = strtok (NULL,":");
		(*notPresentElements)[name]=multiplicity;		
	}
 	for(list<XmlAttribute>::iterator attribute = attributes.begin();attribute != attributes.end(); attribute++){
		bool found=false;
		for(list<string>::iterator allowedElement = allowedElements->begin();allowedElement != allowedElements->end(); allowedElement++){
			char values[200];
			strcpy(values,allowedElement->c_str());
			char* name = strtok (values,":");
			char* multiplicity = strtok (NULL,":");
			if(strcmp(attribute->getName()->c_str(),name)==0){
				found=true;		
				if(strcmp("1",multiplicity)==0||strcmp("0",multiplicity)==0){
					bool exist=false;
					for(list<string>::iterator onlyOneTag = onlyOneMultiplicity->begin();onlyOneTag != onlyOneMultiplicity->end(); onlyOneTag++){
						if(strcmp(onlyOneTag->c_str(),name)==0){	
							exist=true;
						}
					}
					if(exist){
						string error="El attributo '";
						error.append((*attribute->getName()));
						error.append("' se esperaba como attributo de multiplicidad 1 pero se encontro mas de una vez en el elemento '");
						error.append((*element->getName()));
						error.append("'.");
						log->info(error);
					}else{
						onlyOneMultiplicity->insert(onlyOneMultiplicity->end(),name);
					}
				}
				break;
			}
		}
		if(!found){
			string error="El attributo '";
			error.append((*attribute->getName()));
			error.append("' no se esperaba como attributo de '");
			error.append((*element->getName()));
			error.append("'.");
			log->info(error);
		}else{
			map<string,string>::iterator i =notPresentElements->find((*attribute->getName()));
			if(  i != notPresentElements->end() ) 
				notPresentElements->erase(i);
		}
	}
	for(list<XMLElement>::iterator child = childrens.begin();child != childrens.end(); child++){
		bool found=false;
		for(list<string>::iterator allowedElement = allowedElements->begin();allowedElement != allowedElements->end(); allowedElement++){
			char values[200];
			strcpy(values,allowedElement->c_str());
			char* name = strtok (values,":");
			char* multiplicity = strtok (NULL,":");
			if(strcmp(child->getName()->c_str(),name)==0){
				found=true;	
				if(strcmp("1",multiplicity)==0||strcmp("0",multiplicity)==0){
					bool exist=false;
					for(list<string>::iterator onlyOneTag = onlyOneMultiplicity->begin();onlyOneTag != onlyOneMultiplicity->end(); onlyOneTag++){
						if(strcmp(onlyOneTag->c_str(),name)==0){	
							exist=true;
						}
					}
					if(exist){
						string error="El Nodo hijo '";
						error.append((*child->getName()));
						error.append("' se esperaba como nodo de multiplicidad 1 pero se encontro mas de una vez en el elemento '");
						error.append((*element->getName()));
						error.append("'.");
						log->info(error);
					}else{
						onlyOneMultiplicity->insert(onlyOneMultiplicity->end(),name);
					}
				}
				break;
			}
		}
		if(!found){
			string error="El nodo '";
			error.append((*child->getName()));
			error.append("' no se espera como hijo de '");
			error.append((*element->getName()));
			error.append("'.");
			log->info(error);
		}else{
			map<string,string>::iterator i =notPresentElements->find((*child->getName()));
			if(  i != notPresentElements->end() ) 
				notPresentElements->erase(i);
			
				validateChilds(&(*child),deep+1,log);
		}
	}
	map<string,string>::iterator it;
	for (it = notPresentElements->begin(); it != notPresentElements->end(); ++it) {
		
		if( strcmp(it->second.c_str(),"0") != 0 ){
			string error="Se esperaba el nodo '";
			error.append(it->first);
			error.append("' como hijo de '");
			error.append((*element->getName()));
			error.append("' pero no fue encontrado.");
			delete notPresentElements;
			throw XmlSemanticException(error);
			
		}
	}
	delete notPresentElements;
}
void DomValidator::printLoadedStructure(Log* log){
map<int, map<string, list<string>*>*>::iterator it;
	 for (it = this->structure.begin(); it != this->structure.end(); ++it) {
		 map<string, list<string>*>::iterator it2;
		 for (it2 = it->second->begin(); it2 != it->second->end(); ++it2) {
			if(it2->second->empty()){
				string out;
				char* buffer=(char*)malloc(20);
				itoa(it->first,buffer,10);
				out.append(buffer);
				out.append(" -> ");
				out.append(it2->first);
				out.append(" -> empty");
				log->info(out);
			}
			for(list<string>::iterator list_iter = it2->second->begin();list_iter != it2->second->end(); list_iter++){					
				string out;
				char* buffer=(char*)malloc(20);
				itoa(it->first,buffer,10);
				out.append(buffer);
				out.append(" -> ");
				out.append(it2->first);
				out.append(" -> ");
				out.append((*list_iter));
				log->info(out);
			}
		 }
	 }
}
void DomValidator::validateDom(XMLElement & root,Log* log){
	validateChilds(&root,1,log);
}